<?php namespace App\Core\Metadata;

use App\Core\Metadata\Data\FormData;
use App\Core\Metadata\Data\EnvData;
use App\Core\Exceptions\RuntimeError;

trait Filler {
    
    protected $fillKey = 'fill';
    protected $defaultValueKey = 'defaultValue';
    protected $fillTypes = ['primary', 'input', 'default', 'env', 'user'];
    
    public static $GET_USER_DATA_METHOD_NAME_PREFIX = 'getUserData';
    
    public function getPrimaryKey(){
        $fillInfos = $this->getFillInfoByFillType('primary');
        if(count($fillInfos) < 1){
            throw new RuntimeError('Matadata format error.Fill type "primary" is not defined.');
        }
        if(count($fillInfos) > 1){
            throw new RuntimeError('Matadata format error.Fill type "primary" is duplicated defined.');
        }
        $keys = array_keys($fillInfos);
        return array_pop($keys);
    }
    
    public function getFieldKeysByFillType($type, $fields = '*'){
        if(!in_array($type, $this->fillTypes)){
            throw new RuntimeError('Matadata error.Fill type "'.$type.'" is not allowed.');
        }
        $infos = $this->getFillInfoByFillType($type, $fields);
        return array_keys($infos);
    }
    
    public function getFillInfoByFillType($type, $fields = '*'){
        if(!in_array($type, $this->fillTypes)){
            throw new RuntimeError('Matadata error.Fill type "'.$type.'" is not allowed.');
        }
        $fieldKyes = $fields == '*' ? $fields : (is_array($fields) ? $fields : func_get_args());
        $infos = [];
        foreach($this->getFields($fieldKyes) as $key => $field){
            $fillInfo = $this->parseFillInfo($key, $field);
            if($fillInfo['type'] == $type){
                $infos[$key] = $fillInfo;
            }
        }
        return $infos;
    }
    
    //-----------------从各数据源填充数据--------------------
    
    public function getData($fields = '*'){
        $fieldKyes = $fields == '*' ? $fields : (is_array($fields) ? $fields : func_get_args());
        //数据填充
        $inputData = $this->getInputData($fieldKyes);
        $envData = $this->getEnvData($fieldKyes);
        $defaultData = $this->getDefaultData($fieldKyes);
        $userData = $this->getUserData($fieldKyes);
        return array_merge($inputData, $envData, $defaultData, $userData);
    }
    
    public function getInputData($fields = '*'){
        $fieldKyes = $fields == '*' ? $fields : (is_array($fields) ? $fields : func_get_args());
        $inputFields = $this->getFieldKeysByFillType('input', $fieldKyes);
        $fillInfos = $this->getFillInfoByFillType('input', $inputFields);
        $formData = new FormData();
        $inputInfos = $formData->only($inputFields)->gets();
        $data = [];
        foreach($fillInfos as $key => $fillInfo){
            /*if(!isset($inputInfos[$fillInfo['value']])){
                throw new RuntimeError('Input error.Input data "'.$fillInfo['value'].'" is not in Input data.');
            }*/
            $data[$key] = isset($inputInfos[$fillInfo['value']]) ? $inputInfos[$fillInfo['value']] : '';
        }
        return $data;
    }
    
    public function getEnvData($fields = '*'){
        $fieldKyes = $fields == '*' ? $fields : (is_array($fields) ? $fields : func_get_args());
        $fillInfos = $this->getFillInfoByFillType('env', $fieldKyes);
        $envInfo = [];
        foreach($fillInfos as $key => $fillInfo){
            $envInfo[$key] = $fillInfo['value'];
        }
        $envData = new EnvData($envInfo);
        return $envData->gets();
    }
    
    public function getDefaultData($fields = '*'){
        $fieldKyes = $fields == '*' ? $fields : (is_array($fields) ? $fields : func_get_args());
        $fillInfos = $this->getFillInfoByFillType('default', $fieldKyes);
        $data = [];
        foreach($fillInfos as $key => $fillInfo){
            $data[$key] = $fillInfo['value'];
        }
        return $data;
    }
    
    public function getUserData($fields = '*'){
        $fieldKyes = $fields == '*' ? $fields : (is_array($fields) ? $fields : func_get_args());
        $fillInfos = $this->getFillInfoByFillType('user', $fieldKyes);
        $data = [];
        foreach($fillInfos as $key => $fillInfo){
            $method = static::$GET_USER_DATA_METHOD_NAME_PREFIX.ucfirst($fillInfo['value']);
            if(!method_exists($this, $method)){
                throw new RuntimeError('Matadata format error.Method "'.$method.'" is undefined for user data "'+$fillInfo['value']+'".');
            }
            $data[$key] = $this->$method();
        }
        return $data;
    }
    
    public function getPrimaryData($inputKeyName = ''){
        $key = $inputKeyName ? $inputKeyName : $this->getPrimaryKey();
        $formData = new FormData();
        return $formData->get($key);
    }
    
    private function parseFillInfo($key, $field){
        if(!isset($field[$this->fillKey])){
            throw new RuntimeError('Matadata format error.Fill key "'.$this->fillKey.'" not found in  field "'.$key.'".');
        }
        $fill = $field[$this->fillKey];
        if(strpos($fill, ':') === false){
            $type = trim($fill);
            if(!in_array($type, $this->fillTypes)){
                throw new RuntimeError('Matadata format error.Fill type "'.$type.'" is not allowed in field "'.$key.'".');
            }
            if($type == 'input'){
                return ['type' => $type, 'value' => $key];
            }
            if($type != 'primary'){
                throw new RuntimeError('Matadata format error.Fill type "'.$type.'" mast have a default value in field "'.$key.'".');
            }
            return ['type' => trim($fill)];
        }
        if(substr_count($fill, ':') !== 1){
            throw new RuntimeError('Matadata format error.Fill key like "'.$fill.'" is not validated in field "'.$key.'".');
        }
        $fillArr = explode(':', $fill);
        $type = trim($fillArr[0]);
        $value = trim($fillArr[1]);
        if(strlen($type) < 1){
            throw new RuntimeError('Matadata format error.Fill key like "'.$fill.'" is not validated in field "'.$key.'".');
        }
        return ['type' => $type, 'value' => $value];
    }
}